# NeHe Tutorial Lesson: 44 - Lense Flare
#
# Ported to PyOpenGL 2.0 by Brian Leair 2004
#
# This code was created by Jeff Molofee 2000
#
# The port was based on the PyOpenGL tutorials and from
# PyOpenGLContext (tests/glprint.py)
#
# If you've found this code useful, feel free to let me know
# at (Brian Leair telcom_sage@yahoo.com).
#
# See original source and C based tutorial at http://nehe.gamedev.net
#
# Note:
# -----
# This code is not an ideal example of Pythonic coding or use of OO
# techniques. It is a simple and direct exposition of how to use the
# Open GL API in Python via the PyOpenGL package. It also uses GLUT,
# a high quality platform independent library. Due to using these APIs,
# this code is more like a C program using procedural programming.
#
# To run this example you will need:
# Python 	- www.python.org (v 2.3 as of 1/2004)
# PyOpenGL 	- pyopengl.sourceforge.net (v 2.0.1.07 as of 1/2004)
# Numeric Python	- (v.22 of "numpy" as of 1/2004) numpy.sourceforge.net
# Python Image Library	- http://www.pythonware.com/products/pil/
#
# #########################################################
# Please note, don't use PyOpenGL older than 2.0.1.07.
# Older PyOpenGL had a bug glGetFloat () that prevents this
# tutorial from working.
#
#


from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import PIL.Image as Image  # PIL

try:
    import win32api  # GetTickCount ()

    gHaveWin32 = 1
except ImportError:
    gHaveWin32 = 0
import sys
import time  # clock ()
import os
import copy
import math
from math import sqrt, fabs

try:
    import numpy as Numeric
except ImportError:
    try:
        import Numeric
    except ImportError:
        print("This demo requires the numpy or Numeric extension, sorry")
        import sys

        sys.exit()


# The glVector of lesson 44 has three componenets only
# i j k
class glVector:
    # Need to create from a point?
    def __init__(self, *args):
        if len(args) == 3:
            self.i = args[0]
            self.j = args[1]
            self.k = args[2]
            return
        if len(args) == 1:
            # // Assign this vector to the vector passed in.
            src_v = args[0]
            if not isinstance(src_v, glVector):
                raise TypeError("Invalid ctor argument for glVector")
            self.i = src_v.i
            self.j = src_v.j
            self.k = src_v.k
            return
        elif len(args) == 0:
            self.i = 0.
            self.j = 0.
            self.k = 0.
            return
        else:
            raise TypeError("Invalid ctor argument for glVector")

            # def __setattr__ (self, name, value)
            # """ We want to """

    # self.name = value

    def Magnitude(self):
        Mag = math.sqrt(self.i * self.i + self.j * self.j + self.k * self.k)
        return Mag

    def Normalize(self):
        mag = self.Magnitude()
        if mag != 0.0:
            self.i /= mag
            self.j /= mag
            self.k /= mag
        return

    def __mul__(self, scalar):
        result = glVector(self)
        result *= scalar
        return result

    def __imul__(self, other):
        if type(other) == int:
            scalar = other
            self.i *= scalar
            self.j *= scalar
            self.k *= scalar
        elif type(other) == float:
            scalar = other
            self.i *= scalar
            self.j *= scalar
            self.k *= scalar
        else:
            raise TypeError("Invalid type (%s) for multiplication argument" % (str(type(other))))

        return self

    def __add__(self, v):
        result = copy.copy(self)
        result += v
        return result

    def __iadd__(self, v):
        self.i += v.i
        self.j += v.j
        self.k += v.k
        return self

    def __sub__(self, v):
        self.i -= v.i
        self.j -= v.j
        self.k -= v.k
        return self

    def __str__(self):
        return "i=%f j=%f k=%f, magnitude=%f" % (self.i, self.j, self.k, self.Magnitude())


class glPoint:
    def __init__(self, first=0., second=0., third=0.):
        # Allow creation from a vector
        if type(first) == glVector:
            self.x = first.i
            self.y = first.j
            self.z = first.k
        else:
            self.x = first
            self.y = second
            self.z = third

    def __str__(self):
        return "x=%f y=%f z=%f" % (self.x, self.y, self.z)

    def __iadd__(self, other):
        self.x += other.x
        self.y += other.y
        self.z += other.z
        return self

    def __add__(self, other):
        result = glPoint(self.x, self.y, self.z)
        result += other
        return result

    def __sub__(self, other):
        """ Create a vector from the difference between this point and another point """
        result = glVector(self.x - other.x, self.y - other.y, self.z - other.z)
        return result


class glCamera:
    # //////////// CONSTRUCTORS /////////////////////////////////////////
    def __init__(self):
        # // Initalize all our member varibles.
        self.m_MaxPitchRate = 0.0
        self.m_MaxHeadingRate = 0.0
        self.m_HeadingDegrees = 0.0
        self.m_PitchDegrees = 0.0
        self.m_MaxForwardVelocity = 0.0
        self.m_ForwardVelocity = 0.0
        self.m_GlowTexture = None
        # bleair: NOTE that glCamera.cpp has a bug. m_BigGlowTexture isn't initialized.
        # Very minor bug because only in the case where we fail to get an earlier
        # texture will the class potentially read from the uninited memory. Most of
        # the time the field is assigned to straight away in InitGL ().
        self.m_BigGlowTexture = None
        self.m_HaloTexture = None
        self.m_StreakTexture = None
        self.m_MaxPointSize = 0.0
        self.m_Frustum = Numeric.zeros((6, 4), 'f')

        self.m_LightSourcePos = glPoint()
        self.m_Position = glPoint()
        self.m_DirectionVector = glVector()
        self.m_ptIntersect = glPoint()

    def __del__(self):
        self.release()
        return

    def release(self):
        if self.m_GlowTexture is not None: glDeleteTextures(self.m_GlowTexture)
        if self.m_HaloTexture is not None: glDeleteTextures(self.m_HaloTexture)
        if self.m_BigGlowTexture is not None: glDeleteTextures(self.m_BigGlowTexture)
        if self.m_StreakTexture is not None: glDeleteTextures(self.m_StreakTexture)
        return

    def ChangePitch(self, degrees):
        if fabs(degrees) < fabs(self.m_MaxPitchRate):
            # // Our pitch is less than the max pitch rate that we
            # // defined so lets increment it.
            self.m_PitchDegrees += degrees
        else:
            # // Our pitch is greater than the max pitch rate that
            # // we defined so we can only increment our pitch by the
            # // maximum allowed value.
            if degrees < 0:
                # // We are pitching down so decrement
                self.m_PitchDegrees -= self.m_MaxPitchRate
            else:
                # // We are pitching up so increment
                self.m_PitchDegrees += self.m_MaxPitchRate

        # // We don't want our pitch to run away from us. Although it
        # // really doesn't matter I prefer to have my pitch degrees
        # // within the range of -360.0f to 360.0f
        if self.m_PitchDegrees > 360.0: self.m_PitchDegrees -= 360.0
        elif self.m_PitchDegrees < -360.0: self.m_PitchDegrees += 360.0

        return

    def ChangeHeading(self, degrees):
        if fabs(degrees) < fabs(self.m_MaxHeadingRate):
            # // Our Heading is less than the max heading rate that we
            # // defined so lets increment it but first we must check
            # // to see if we are inverted so that our heading will not
            # // become inverted.
            if (90 < self.m_PitchDegrees < 270) or (-90 > self.m_PitchDegrees > -270):
                self.m_HeadingDegrees -= degrees
            else:
                self.m_HeadingDegrees += degrees
        else:
            # // Our heading is greater than the max heading rate that
            # // we defined so we can only increment our heading by the
            # // maximum allowed value.
            if degrees < 0:
                # // Check to see if we are upside down.
                if (90 < self.m_PitchDegrees < 270) or (-90 > self.m_PitchDegrees > -270):
                    # // Ok we would normally decrement here but since we are upside
                    # // down then we need to increment our heading
                    self.m_HeadingDegrees += self.m_MaxHeadingRate
                else:
                    # // We are not upside down so decrement as usual
                    self.m_HeadingDegrees -= self.m_MaxHeadingRate
            else:
                # // Check to see if we are upside down.
                if (90 < self.m_PitchDegrees < 270) or (-90 > self.m_PitchDegrees > -270):
                    # // Ok we would normally increment here but since we are upside
                    # // down then we need to decrement our heading.
                    self.m_HeadingDegrees -= self.m_MaxHeadingRate
                else:
                    # // We are not upside down so increment as usual.
                    self.m_HeadingDegrees += self.m_MaxHeadingRate

        # // We don't want our heading to run away from us either. Although it
        # // really doesn't matter I prefer to have my heading degrees
        # // within the range of -360.0f to 360.0f
        if self.m_HeadingDegrees > 360.0: self.m_HeadingDegrees -= 360.0
        elif self.m_HeadingDegrees < -360.0: self.m_HeadingDegrees += 360.0

        return

    # //////////// FUNCTIONS TO CHANGE CAMERA ORIENTATION AND SPEED /////
    def ChangeVelocity(self, vel):
        if fabs(vel) < fabs(self.m_MaxForwardVelocity):
            # // Our velocity is less than the max velocity increment that we
            # // defined so lets increment it.
            self.m_ForwardVelocity += vel
        else:
            # // Our velocity is greater than the max velocity increment that
            # // we defined so we can only increment our velocity by the
            # // maximum allowed value.
            if vel < 0:
                # // We are slowing down so decrement
                self.m_ForwardVelocity -= -self.m_MaxForwardVelocity
            else:
                # // We are speeding up so increment
                self.m_ForwardVelocity += self.m_MaxForwardVelocity

        return

    def UpdateFrustum(self):
        """ // I found this code here: http://www.markmorley.com/opengl/frustumculling.html
        // and decided to make it part of
        // the camera class just in case I might want to rotate
        // and translate the projection matrix. This code will
        // make sure that the Frustum is updated correctly but
        // this member is computational expensive with:
        // 82 muliplications, 72 additions, 24 divisions, and
        // 12 subtractions for a total of 190 operations. Ouch! """

        # /* Get the current PROJECTION matrix from OpenGL */
        proj = glGetFloatv(GL_PROJECTION_MATRIX)

        # /* Get the current MODELVIEW matrix from OpenGL */
        modl = glGetFloatv(GL_MODELVIEW_MATRIX)

        # /* Combine the two matrices (multiply projection by modelview) */
        # Careful, Note, that replication is simple scalars is OK, but replicate of objects
        # and lists is very bad.
        clip = [None, ] * 16
        # clip = Numeric.zeros ( (16), 'f')
        clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]
        clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]
        clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]
        clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]

        clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]
        clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]
        clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]
        clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]

        clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]
        clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]
        clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]
        clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]

        clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]
        clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]
        clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]
        clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]

        # ### Use a shortened name to reference to our camera's Frustum (does
        # ### not copy anything, just a ref to make code less wordy
        Frustum = self.m_Frustum

        # /* Extract the numbers for the RIGHT plane */
        Frustum[0][0] = clip[3] - clip[0]
        Frustum[0][1] = clip[7] - clip[4]
        Frustum[0][2] = clip[11] - clip[8]
        Frustum[0][3] = clip[15] - clip[12]

        # /* Normalize the result */
        t = (sqrt(Frustum[0][0] * Frustum[0][0] + Frustum[0][1] * Frustum[0][1] + Frustum[0][2] * Frustum[0][2]))
        Frustum[0][0] /= t
        Frustum[0][1] /= t
        Frustum[0][2] /= t
        Frustum[0][3] /= t

        # /* Extract the numbers for the LEFT plane */
        Frustum[1][0] = clip[3] + clip[0]
        Frustum[1][1] = clip[7] + clip[4]
        Frustum[1][2] = clip[11] + clip[8]
        Frustum[1][3] = clip[15] + clip[12]

        # /* Normalize the result */
        t = sqrt(Frustum[1][0] * Frustum[1][0] + Frustum[1][1] * Frustum[1][1] + Frustum[1][2] * Frustum[1][2])
        Frustum[1][0] /= t
        Frustum[1][1] /= t
        Frustum[1][2] /= t
        Frustum[1][3] /= t

        # /* Extract the BOTTOM plane */
        Frustum[2][0] = clip[3] + clip[1]
        Frustum[2][1] = clip[7] + clip[5]
        Frustum[2][2] = clip[11] + clip[9]
        Frustum[2][3] = clip[15] + clip[13]

        # /* Normalize the result */
        t = sqrt(Frustum[2][0] * Frustum[2][0] + Frustum[2][1] * Frustum[2][1] + Frustum[2][2] * Frustum[2][2])
        Frustum[2][0] /= t
        Frustum[2][1] /= t
        Frustum[2][2] /= t
        Frustum[2][3] /= t

        # /* Extract the TOP plane */
        Frustum[3][0] = clip[3] - clip[1]
        Frustum[3][1] = clip[7] - clip[5]
        Frustum[3][2] = clip[11] - clip[9]
        Frustum[3][3] = clip[15] - clip[13]

        # /* Normalize the result */
        t = sqrt(Frustum[3][0] * Frustum[3][0] + Frustum[3][1] * Frustum[3][1] + Frustum[3][2] * Frustum[3][2])
        Frustum[3][0] /= t
        Frustum[3][1] /= t
        Frustum[3][2] /= t
        Frustum[3][3] /= t

        # /* Extract the FAR plane */
        Frustum[4][0] = clip[3] - clip[2]
        Frustum[4][1] = clip[7] - clip[6]
        Frustum[4][2] = clip[11] - clip[10]
        Frustum[4][3] = clip[15] - clip[14]

        # /* Normalize the result */
        t = sqrt(Frustum[4][0] * Frustum[4][0] + Frustum[4][1] * Frustum[4][1] + Frustum[4][2] * Frustum[4][2])
        Frustum[4][0] /= t
        Frustum[4][1] /= t
        Frustum[4][2] /= t
        Frustum[4][3] /= t

        # /* Extract the NEAR plane */
        Frustum[5][0] = clip[3] + clip[2]
        Frustum[5][1] = clip[7] + clip[6]
        Frustum[5][2] = clip[11] + clip[10]
        Frustum[5][3] = clip[15] + clip[14]

        # /* Normalize the result */
        t = sqrt(Frustum[5][0] * Frustum[5][0] + Frustum[5][1] * Frustum[5][1] + Frustum[5][2] * Frustum[5][2])
        Frustum[5][0] /= t
        Frustum[5][1] /= t
        Frustum[5][2] /= t
        Frustum[5][3] /= t

        return

    # //////////// FUNCTIONS TO UPDATE THE FRUSTUM //////////////////////
    def UpdateFrustumFaster(self):
        """ // This is the much faster version of the above member
        // function, however the speed increase is not gained
        // without a cost. If you rotate or translate the projection
        // matrix then this member will not work correctly. That is acceptable
        // in my book considering I very rarely do such a thing.
        // This function has far fewer operations in it and I
        // shaved off 2 square root functions by passing in the
        // near and far values. This member has:
        // 38 muliplications, 28 additions, 24 divisions, and
        // 12 subtractions for a total of 102 operations. Still hurts
        // but at least it is decent now. In practice this will
        // run about 2 times faster than the above function. """

        # /* Get the current PROJECTION matrix from OpenGL */
        proj = glGetFloatv(GL_PROJECTION_MATRIX)

        # /* Get the current MODELVIEW matrix from OpenGL */
        modl = glGetFloatv(GL_MODELVIEW_MATRIX)

        # /* Combine the two matrices (multiply projection by modelview)
        # but keep in mind this function will only work if you do NOT
        # rotate or translate your projection matrix                  */
        clip = [0, ] * 16
        modl_row1 = modl[0]
        clip[0] = modl[0][0] * proj[0][0]
        clip[1] = modl[0][1] * proj[1][1]
        clip[2] = modl[0][2] * proj[2][2] + modl_row1[3] * proj[3][2]
        clip[3] = modl[0][2] * proj[2][3]

        modl_row2 = modl[1]
        clip[4] = modl_row2[0] * proj[0][0]
        clip[5] = modl_row2[1] * proj[1][1]
        clip[6] = modl_row2[2] * proj[2][2] + modl_row2[3] * proj[3][2]
        clip[7] = modl_row2[2] * proj[2][3]

        modl_row3 = modl[2]
        clip[8] = modl_row3[0] * proj[0][0]
        clip[9] = modl_row3[1] * proj[1][1]
        clip[10] = modl_row3[2] * proj[2][2] + modl_row3[3] * proj[3][2]
        clip[11] = modl_row3[2] * proj[2][3]

        modl_row4 = modl[3]
        clip[12] = modl_row4[0] * proj[0][0]
        clip[13] = modl_row4[1] * proj[1][1]
        clip[14] = modl_row4[2] * proj[2][2] + modl_row4[3] * proj[3][2]
        clip[15] = modl_row4[2] * proj[2][3]

        # ### Use a shortened name to reference to our camera's Frustum (does
        # ### not copy anything, just a ref to make code less wordy
        Frustum = self.m_Frustum

        # /* Extract the numbers for the RIGHT plane */
        Frustum[0][0] = clip[3] - clip[0]
        Frustum[0][1] = clip[7] - clip[4]
        Frustum[0][2] = clip[11] - clip[8]
        Frustum[0][3] = clip[15] - clip[12]

        # /* Normalize the result */
        t = sqrt((Frustum[0][0] * Frustum[0][0]) + (Frustum[0][1] * Frustum[0][1]) + (Frustum[0][2] * Frustum[0][2]))
        Frustum[0][0] /= t
        Frustum[0][1] /= t
        Frustum[0][2] /= t
        Frustum[0][3] /= t

        # /* Extract the numbers for the LEFT plane */
        Frustum[1][0] = clip[3] + clip[0]
        Frustum[1][1] = clip[7] + clip[4]
        Frustum[1][2] = clip[11] + clip[8]
        Frustum[1][3] = clip[15] + clip[12]

        # /* Normalize the result */
        t = sqrt(Frustum[1][0] * Frustum[1][0] + Frustum[1][1] * Frustum[1][1] + Frustum[1][2] * Frustum[1][2])
        Frustum[1][0] /= t
        Frustum[1][1] /= t
        Frustum[1][2] /= t
        Frustum[1][3] /= t

        # /* Extract the BOTTOM plane */
        Frustum[2][0] = clip[3] + clip[1]
        Frustum[2][1] = clip[7] + clip[5]
        Frustum[2][2] = clip[11] + clip[9]
        Frustum[2][3] = clip[15] + clip[13]

        # /* Normalize the result */
        t = sqrt(Frustum[2][0] * Frustum[2][0] + Frustum[2][1] * Frustum[2][1] + Frustum[2][2] * Frustum[2][2])
        Frustum[2][0] /= t
        Frustum[2][1] /= t
        Frustum[2][2] /= t
        Frustum[2][3] /= t

        # /* Extract the TOP plane */
        Frustum[3][0] = clip[3] - clip[1]
        Frustum[3][1] = clip[7] - clip[5]
        Frustum[3][2] = clip[11] - clip[9]
        Frustum[3][3] = clip[15] - clip[13]

        # /* Normalize the result */
        t = sqrt(Frustum[3][0] * Frustum[3][0] + Frustum[3][1] * Frustum[3][1] + Frustum[3][2] * Frustum[3][2])
        Frustum[3][0] /= t
        Frustum[3][1] /= t
        Frustum[3][2] /= t
        Frustum[3][3] /= t

        # /* Extract the FAR plane */
        Frustum[4][0] = clip[3] - clip[2]
        Frustum[4][1] = clip[7] - clip[6]
        Frustum[4][2] = clip[11] - clip[10]
        Frustum[4][3] = clip[15] - clip[14]

        # /* Normalize the result */
        t = sqrt((Frustum[4][0] * Frustum[4][0]) + (Frustum[4][1] * Frustum[4][1]) + (Frustum[4][2] * Frustum[4][2]))
        Frustum[4][0] /= t
        Frustum[4][1] /= t
        Frustum[4][2] /= t
        Frustum[4][3] /= t

        # /* Extract the NEAR plane */
        Frustum[5][0] = clip[3] + clip[2]
        Frustum[5][1] = clip[7] + clip[6]
        Frustum[5][2] = clip[11] + clip[10]
        Frustum[5][3] = clip[15] + clip[14]

        # /* Normalize the result */
        t = sqrt(Frustum[5][0] * Frustum[5][0] + Frustum[5][1] * Frustum[5][1] + Frustum[5][2] * Frustum[5][2])
        Frustum[5][0] /= t
        Frustum[5][1] /= t
        Frustum[5][2] /= t
        Frustum[5][3] /= t

        return


    # //////////// FRUSTUM TESTING FUNCTIONS ////////////////////////////
    def SphereInFrustum(self, p, Radius):
        """ // This member function checks to see if a sphere is in
            // the viewing volume.   """

        Frustum = self.m_Frustum
        # // The idea here is the same as the PointInFrustum function.
        if Radius != 0:
            for i in range(6):
                # // If the point is outside of the plane then its not in the viewing volume.
                if Frustum[i][0] * p.x + Frustum[i][1] * p.y + Frustum[i][2] * p.z + Frustum[i][3] <= -Radius:
                    return False
        else:
            # // The idea here is the same as the PointInFrustum function.
            for i in range(6):
                # // If the point is outside of the plane then its not in the viewing volume.
                if Frustum[i][0] * p.x + Frustum[i][1] * p.y + Frustum[i][2] * p.z + Frustum[i][3] <= 0:
                    return False

        return True

    def PointInFrustum(self, x, y, z):
        """ // This member fuction checks to see if a point is in
            // the viewing volume. """

        # // The idea behind this algorithum is that if the point
        # // is inside all 6 clipping planes then it is inside our
        # // viewing volume so we can return true.

        Frustum = self.m_Frustum
        # // Loop through all our clipping planes
        for i in range(6):
            # // If the point is outside of the plane then its not in the viewing volume.
            if Frustum[i][0] * x + Frustum[i][1] * y + Frustum[i][2] * z + Frustum[i][3] <= 0:
                return False

        return True

    # /////////// OCCLUSION TESTING FUNCTIONS ///////////////////////////
    def IsOccluded(self, p):
        # // Now we will ask OGL to project some geometry for us using the gluProject function.
        # // Practically we ask OGL to guess where a point in space will be projected in our current viewport,
        # // using arbitrary viewport and transform matrices we pass to the function.
        # // If we pass to the function the current matrices  (retrievede with the glGet funcs)
        # // we will have the real position on screen where the dot will be drawn.
        # // The interesting part is that we also get a Z value back, this means that
        # // reading the REAL buffer for Z values we can discover if the flare is in front or
        # // if it's occluded by some objects.
        # ### This function should be a flat function, not a function of the camera as we
        # ### use the immediate GL rendering state entirely.

        # ### Viewport is the rectangle of window pixels that OpenGL is rasterizing into.
        viewport = glGetIntegerv(GL_VIEWPORT)  # //get actual viewport
        mvmatrix = glGetDoublev(GL_MODELVIEW_MATRIX)  # //get actual model view matrix
        projmatrix = glGetDoublev(GL_PROJECTION_MATRIX)  # //get actual projiection matrix

        # // this asks OGL to guess the 2d position of a 3d point inside the viewport
        winx, winy, winz = gluProject(p.x, p.y, p.z, mvmatrix, projmatrix, viewport)
        flareZ = winz

        # // we read back one pixel from th depth buffer (exactly where our flare should be drawn)
        glPixelStorei(GL_PACK_ALIGNMENT, 1)

        # PyOpenGL 2.0.1.07 bug, Only the type clarified function works.
        # bufferZ = glReadPixels(int(winx), int(winy),1,1,GL_DEPTH_COMPONENT, GL_FLOAT)
        bufferZ = glReadPixelsf(int(winx), int(winy), 1, 1, GL_DEPTH_COMPONENT)

        # // if the buffer Z is lower than our flare guessed Z then don't draw
        # // this means there is something in front of our flare
        if (bufferZ[0][0] < flareZ):
            return True
        else:
            return False

    # //////////// FUNCTIONS TO RENDER LENS FLARES //////////////////////
    def RenderLensFlare(self):
        # // Draw the flare only If the light source is in our line of sight (inside the Frustum)
        if self.SphereInFrustum(self.m_LightSourcePos, 1.0):
            # Vector pointing from the light's position toward the camera's position (the camera might
            # be pointing elsewhere, this vector is pointing from the light to the camera)
            self.vLightSourceToCamera = self.m_Position - self.m_LightSourcePos  # // Lets compute the vector that points to the camera from
            # // the light source.

            Length = self.vLightSourceToCamera.Magnitude()  # // Save the length we will need it in a minute

            # Move down our look-toward direction vector. Move down the look-toward the same dist. as the
            # distance between camera and the light.
            intersect = self.m_DirectionVector * Length
            self.m_ptIntersect = glPoint(intersect.i, intersect.j, intersect.k)
            # // Now lets find an point along the cameras direction
            # // vector that we can use as an intersection point.
            # // Lets translate down this vector the same distance
            # // that the camera is away from the light source.
            ptIntersect = self.m_ptIntersect
            # Did the motion in the correct direction above, now translate the intersection position
            # relative to our camera location.
            ptIntersect += self.m_Position

            self.vLightSourceToIntersect = ptIntersect - self.m_LightSourcePos  # // Lets compute the vector that points to the Intersect
            # // point from the light source

            Length = self.vLightSourceToIntersect.Magnitude()  # // Save the length we will need it later.
            self.vLightSourceToIntersect.Normalize()  # // Normalize the vector so its unit length
            vLightSourceToIntersect = self.vLightSourceToIntersect

            glEnable(GL_BLEND)  # // You should already know what this does
            glBlendFunc(GL_SRC_ALPHA, GL_ONE)  # // You should already know what this does
            glDisable(GL_DEPTH_TEST)  # // You should already know what this does
            glEnable(GL_TEXTURE_2D)  # // You should already know what this does

            # /////////// Differenet Color Glows & Streaks /////////////////////
            # //RenderBigGlow(1.0f, 1.0f, 1.0f, 1.0f, m_LightSourcePos, 1.0f)
            # //RenderStreaks(1.0f, 1.0f, 0.8f, 1.0f, m_LightSourcePos, 0.7f)
            # //
            # //RenderBigGlow(1.0f, 0.9f, 1.0f, 1.0f, m_LightSourcePos, 1.0f)
            # //RenderStreaks(1.0f, 0.9f, 1.0f, 1.0f, m_LightSourcePos, 0.7f)
            # //////////////////////////////////////////////////////////////////


            # //########################## NEW STUFF ##################################

            if (not self.IsOccluded(self.m_LightSourcePos)):  # //Check if the center of the flare is occluded
                # // Render the large hazy glow
                self.RenderBigGlow(0.60, 0.60, 0.8, 1.0, self.m_LightSourcePos, 16.0)
                # // Render the streaks
                self.RenderStreaks(0.60, 0.60, 0.8, 1.0, self.m_LightSourcePos, 16.0)
                # // Render the small Glow
                self.RenderGlow(0.8, 0.8, 1.0, 0.5, self.m_LightSourcePos, 3.5)

                pt = glPoint(vLightSourceToIntersect * (Length * 0.1))  # // Lets compute a point that is 20%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.9, 0.6, 0.4, 0.5, pt, 0.6)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 0.15))  # // Lets compute a point that is 30%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.8, 0.5, 0.6, 0.5, pt, 1.7)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.175))  # // Lets compute a point that is 35%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.9, 0.2, 0.1, 0.5, pt, 0.83)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.285))  # // Lets compute a point that is 57%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.7, 0.7, 0.4, 0.5, pt, 1.6)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.2755))  # // Lets compute a point that is 55.1%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.9, 0.9, 0.2, 0.5, pt, 0.8)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 0.4775))  # // Lets compute a point that is 95.5%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.93, 0.82, 0.73, 0.5, pt, 1.0)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 0.49))  # // Lets compute a point that is 98%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.7, 0.6, 0.5, 0.5, pt, 1.4)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.65))  # // Lets compute a point that is 130%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.7, 0.8, 0.3, 0.5, pt, 1.8)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 0.63))  # // Lets compute a point that is 126%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.4, 0.3, 0.2, 0.5, pt, 1.4)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 0.8))  # // Lets compute a point that is 160%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.7, 0.5, 0.5, 0.5, pt, 1.4)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.7825))  # // Lets compute a point that is 156.5%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.8, 0.5, 0.1, 0.5, pt, 0.6)  # // Render the small Glow

                pt = glPoint(vLightSourceToIntersect * (Length * 1.0))  # // Lets compute a point that is 200%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderHalo(0.5, 0.5, 0.7, 0.5, pt, 1.7)  # // Render the a Halo

                pt = glPoint(vLightSourceToIntersect * (Length * 0.975))  # // Lets compute a point that is 195%
                pt += self.m_LightSourcePos  # // away from the light source in the
                # // direction of the intersection point.

                self.RenderGlow(0.4, 0.1, 0.9, 0.5, pt, 2.0)  # // Render the small Glow

            glDisable(GL_BLEND)  # // You should already know what this does
            glEnable(GL_DEPTH_TEST)  # // You should already know what this does
            glDisable(GL_TEXTURE_2D)  # // You should already know what this does
        return

    def RenderHalo(self, r, g, b, a, p, scale):
        self.RenderFlareTexture(self.m_HaloTexture, r, g, b, a, p, scale)
        return

    def RenderGlow(self, r, g, b, a, p, scale):
        self.RenderFlareTexture(self.m_GlowTexture, r, g, b, a, p, scale)
        return

    def RenderBigGlow(self, r, g, b, a, p, scale):
        self.RenderFlareTexture(self.m_BigGlowTexture, r, g, b, a, p, scale)
        return

    def RenderStreaks(self, r, g, b, a, p, scale):
        self.RenderFlareTexture(self.m_StreakTexture, r, g, b, a, p, scale)
        return

    def RenderFlareTexture(self, tex_ID, r, g, b, a, p, scale):
        # bleair: Duplicate functions all the same except for the texture to bind to.

        q = []
        q.append(glPoint())
        q.append(glPoint())
        q.append(glPoint())
        q.append(glPoint())
        # // Basically we are just going to make a 2D box
        # // from four points we don't need a z coord because
        # // we are rotating the camera by the inverse so the
        # // texture mapped quads will always face us.

        q[0].x = (p.x - scale)  # // Set the x coordinate -scale units from the center point.
        q[0].y = (p.y - scale)  # // Set the y coordinate -scale units from the center point.

        q[1].x = (p.x - scale)  # // Set the x coordinate -scale units from the center point.
        q[1].y = (p.y + scale)  # // Set the y coordinate scale units from the center point.

        q[2].x = (p.x + scale)  # // Set the x coordinate scale units from the center point.
        q[2].y = (p.y - scale)  # // Set the y coordinate -scale units from the center point.

        q[3].x = (p.x + scale)  # // Set the x coordinate scale units from the center point.
        q[3].y = (p.y + scale)  # // Set the y coordinate scale units from the center point.

        glPushMatrix()  # // Save the model view matrix
        glTranslatef(p.x, p.y, p.z)  # // Translate to our point
        glRotatef(-self.m_HeadingDegrees, 0.0, 1.0, 0.0)
        glRotatef(-self.m_PitchDegrees, 1.0, 0.0, 0.0)
        glBindTexture(GL_TEXTURE_2D, tex_ID)  # // Bind to the Big Glow texture
        glColor4f(r, g, b, a)  # // Set the color since the texture is a gray scale

        glBegin(GL_TRIANGLE_STRIP)  # // Draw the Big Glow on a Triangle Strip
        glTexCoord2f(0.0, 0.0)
        glVertex2f(q[0].x, q[0].y)
        glTexCoord2f(0.0, 1.0)
        glVertex2f(q[1].x, q[1].y)
        glTexCoord2f(1.0, 0.0)
        glVertex2f(q[2].x, q[2].y)
        glTexCoord2f(1.0, 1.0)
        glVertex2f(q[3].x, q[3].y)
        glEnd()
        glPopMatrix()  # // Restore the model view matrix
        return


    def SetPrespective(self):
        # Matrix = [0] * 16 					# // A (list) array to hold the model view matrix.

        # However the MODELVIEW was oriented, we now rotate it based upon our Camer object's state.
        # // Going to use glRotate to calculate our direction vector
        glRotatef(self.m_HeadingDegrees, 0.0, 1.0, 0.0)  # turn your head left/right (around y axe)
        glRotatef(self.m_PitchDegrees, 1.0, 0.0, 0.0)  # nod your head up/down (around x axe)

        # // Get the resulting matrix from OpenGL it will have our
        # // direction vector in the 3rd row.
        Matrix = glGetFloatv(GL_MODELVIEW_MATRIX)

        # // Get the direction vector from the matrix. Element 10 must
        # // be inverted!
        self.m_DirectionVector.i = Matrix[2][0]  # [8]
        self.m_DirectionVector.j = Matrix[2][1]  # [9]
        self.m_DirectionVector.k = -Matrix[2][2]  # [10]

        # #### bleair: no need to do this as this. Previous rotates already here (because
        # #### all invocations have the modelview at identity.
        # #### Suspect this was just a bit of code that was mvoed up and not deleted here.
        # // Ok erase the results of the last computation.
        glLoadIdentity()

        # // Rotate the scene to get the right orientation.
        glRotatef(self.m_PitchDegrees, 1.0, 0.0, 0.0)
        glRotatef(self.m_HeadingDegrees, 0.0, 1.0, 0.0)

        # // A vector to hold our cameras direction * the forward velocity
        # // we don't want to destory the Direction vector by using it instead.
        # // Scale the direction by our speed.
        v = copy.copy(self.m_DirectionVector)
        v *= self.m_ForwardVelocity

        # // Increment our position by the vector
        self.m_Position.x += v.i
        self.m_Position.y += v.j
        self.m_Position.z += v.k

        # // Translate to our new position.
        glTranslatef(-self.m_Position.x, -self.m_Position.y, -self.m_Position.z)
        return


class glFont:
    def __init__(self):
        self.m_FontTexture = 0
        self.m_ListBase = 0

    def __del__(self):
        self.release()
        return

    def release(self):
        """ We've made a separate resoruce-deallocation method
            so that the client can return system resources when the
            want to explcitly. Python will eventually garbage collect,
            once all refs to the object go away. This method
            allows the client to retain the object, and yet free up the
            gl resources.
        """
        if self.m_FontTexture != 0:
            glDeleteTextures(self.m_FontTexture)
        if self.m_ListBase != 0:
            glDeleteLists(self.m_ListBase, 256)
        return

    def SetFontTexture(self, tex):
        if tex != 0:  # // If the texture is valid
            self.m_FontTexture = tex  # // Set the font texture
        else:
            # Client should not pass an invalid texture.
            raise RuntimeError("SetFontTexture passed invalid texture (ID == 0)")
        return

    def BuildFont(self, Scale):
        self.m_ListBase = glGenLists(256)  # // Creating 256 Display Lists
        if self.m_FontTexture != 0:
            glBindTexture(GL_TEXTURE_2D, self.m_FontTexture)  # // Select Our Font Texture
            for loop in range(256):  # // Loop Through All 256 Lists
                cx = float(loop % 16) / 16.0  # // X Position Of Current Character
                cy = float(loop / 16) / 16.0  # // Y Position Of Current Character

                glNewList(self.m_ListBase + loop, GL_COMPILE)  # // Start Building A List
                # List start
                glBegin(GL_QUADS)  # // Use A Quad For Each Character
                glTexCoord2f(cx, 1 - cy - 0.0625)  # // Texture Coord (Bottom Left)
                glVertex2f(0, 0)  # // Vertex Coord (Bottom Left)
                glTexCoord2f(cx + 0.0625, 1 - cy - 0.0625)  # // Texture Coord (Bottom Right)
                glVertex2f(16 * Scale, 0)  # // Vertex Coord (Bottom Right)
                glTexCoord2f(cx + 0.0625, 1 - cy)  # // Texture Coord (Top Right)
                glVertex2f(16 * Scale, 16 * Scale)  # // Vertex Coord (Top Right)
                glTexCoord2f(cx, 1 - cy)  # // Texture Coord (Top Left)
                glVertex2f(0, 16 * Scale)  # // Vertex Coord (Top Left)
                glEnd()  # // Done Building Our Quad (Character)
                glTranslated(10 * Scale, 0, 0)  # // Move To The Right Of The Character
                glEndList()  # // Done Building The Display List
                # List end

    def glPrintf(self, x, y, set, text):
        glEnable(GL_TEXTURE_2D)  # // Enable 2d Textures
        glEnable(GL_BLEND)  # // Enable Blending
        glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
        glBindTexture(GL_TEXTURE_2D, self.m_FontTexture)  # // Select Our Font Texture
        glDisable(GL_DEPTH_TEST)  # // Disables Depth Testing

        glMatrixMode(GL_PROJECTION)  # // Select The Projection Matrix
        glPushMatrix()  # // Store The Projection Matrix
        glLoadIdentity()  # // Reset The Projection Matrix
        glOrtho(0, self.m_WindowWidth, 0, self.m_WindowHeight, -1, 1)  # // Set Up An Ortho Screen
        glMatrixMode(GL_MODELVIEW)  # // Select The Modelview Matrix
        glPushMatrix()  # // Store The Modelview Matrix
        glLoadIdentity()  # // Reset The Modelview Matrix
        glTranslated(x, y, 0)  # // Position The Text (0,0 - Bottom Left)
        glListBase(self.m_ListBase - 32 + (128 * set))  # // Choose The Font Set (0 or 1)
        # glCallLists(len(text),GL_BYTE,text)						# // Write The Text To The Screen
        # function can figure out the count and TYP
        glCallLists(text)  # // Write The Text To The Screen

        glMatrixMode(GL_PROJECTION)  # // Select The Projection Matrix
        glPopMatrix()  # // Restore The Old Projection Matrix
        glMatrixMode(GL_MODELVIEW)  # // Select The Modelview Matrix
        glPopMatrix()  # // Restore The Old Projection Matrix
        glEnable(GL_DEPTH_TEST)
        glDisable(GL_BLEND)
        glDisable(GL_TEXTURE_2D)
        return

    def SetWindowSize(self, width, height):
        self.m_WindowWidth = width
        self.m_WindowHeight = height
        return

    def GetTexture(self):
        return self.m_FontTexture

    def GetListBase(self):
        return self.m_ListBase


"""
# *********************** Globals ***********************
# Python 2.2 defines these directly
try:
    True
except NameError:
    True = 1==1
    False = 1==0
"""

# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = b'\033'

# Number of the glut window.
window = 0
base = None

gInfoOn = False
gFrames = 0
gStartTime = -1
gCurrentTime = -1
gFPS = -1
gCamera = None

# //##################  NEW STUFF  ##################################

qobj = None  # //the quadric for our cylinder
gcylList = None


def LoadTexture(path):
    """ // Load Image And Convert To A Texture
    path can be a relative path, or a fully qualified path.
    returns tuple of status and ID:
    returns False if the requested image couldn't loaded as a texture
    returns True and the texture ID if image was loaded
    """
    # Catch exception here if image file couldn't be loaded
    try:
        # Note, NYI, path specified as URL's could be access using python url lib
        # OleLoadPicturePath () supports url paths, but that capability isn't critcial to this tutorial.
        Picture = Image.open(path)
    except:
        return False, 0

    glMaxTexDim = glGetIntegerv(GL_MAX_TEXTURE_SIZE)

    WidthPixels = Picture.size[0]
    HeightPixels = Picture.size[1]

    if ((WidthPixels > glMaxTexDim) or (HeightPixels > glMaxTexDim)):
        # The image file is too large. Shrink it to fit within the texture dimensions
        # support by our rendering context for a GL texture.
        # Note, Feel free to experiemnt and force a resize by placing a small val into
        # glMaxTexDim (e.g. 32,64,128).
        raise RuntimeError(
            "Texture image (%d by %d) is larger than supported by GL %d." % (WidthPixels, HeightPixels, glMaxTexDim))

    # Create a raw string from the image data - data will be unsigned bytes
    # RGBpad, no stride (0), and first line is top of image (-1)
    pBits = Picture.tostring("raw", "RGBX", 0, -1)

    # // Typical Texture Generation Using Data From The Bitmap
    texid = glGenTextures(1)  # // Create The Texture
    glBindTexture(GL_TEXTURE_2D, texid)  # // Bind To The Texture ID
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                    GL_LINEAR)  # // (Modify This For The Type Of Filtering You Want)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                    GL_LINEAR)  # // (Modify This For The Type Of Filtering You Want)

    # // (Modify This If You Want Mipmaps)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, WidthPixels, HeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits)

    # Cleanup (this would all happen automatically upon return... just spelling it out)
    # // Decrements IPicture Reference Count
    Picture = None
    return True, texid  # // Return True (All Good)


# A general OpenGL initialization function.  Sets all of the initial parameters.
def InitGL(Width, Height):  # We call this right after our OpenGL window is created.
    global gFont, gCamera, gStartTime, gcylList, qobj

    glShadeModel(GL_SMOOTH)  # Enables Smooth Color Shading
    glClearColor(0.0, 0.0, 0.0, 0.5)  # This Will Clear The Background Color To Black
    glClearDepth(1.0)  # Enables Clearing Of The Depth Buffer
    glEnable(GL_DEPTH_TEST)  # Enables Depth Testing
    glDepthFunc(GL_LEQUAL)  # The Type Of Depth Test To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)  # Really Nice Perspective Calculations

    status, tex = LoadTexture(os.path.join("Art", "Font.bmp"))
    if (status):
        gFont = glFont()
        gFont.SetFontTexture(tex)
        gFont.SetWindowSize(1024, 768)
        gFont.BuildFont(1.0)
    else:
        raise RuntimeError("Failed to build font 'Art\\Font.bmp'")

    gCamera = glCamera()
    gCamera.m_MaxHeadingRate = 1.0  # // Set our Maximum rates for the camera
    gCamera.m_MaxPitchRate = 1.0  # // Set our Maximum rates for the camera
    gCamera.m_HeadingDegrees = 0.0  # // Set our Maximum rates for the camera

    # // Try and load the HardGlow texture tell the user if we can't find it then quit
    status, gCamera.m_GlowTexture = LoadTexture(os.path.join("Art", "HardGlow2.bmp"))
    if (not status):
        raise RuntimeError("Failed to load Hard Glow texture.")

    # // Try and load the BigGlow texture tell the user if we can't find it then quit
    status, gCamera.m_BigGlowTexture = LoadTexture(os.path.join("Art", "BigGlow3.bmp"))
    if (not status):
        raise RuntimeError("Failed to load Big Glow texture.")

    # // Try and load the Halo texture tell the user if we can't find it then quit
    status, gCamera.m_HaloTexture = LoadTexture(os.path.join("Art", "Halo3.bmp"))
    if (not status):
        raise RuntimeError("Failed to load Halo texture.")

    # // Try and load the Streaks texture tell the user if we can't find it then quit
    status, gCamera.m_StreakTexture = LoadTexture(os.path.join("Art", "Streaks4.bmp"))
    if (not status):
        raise RuntimeError("Failed to load Streaks texture.")

    # //##################  NEW STUFF  ##################################

    # // Just create a cylinder that will be used as occluder object
    gcylList = glGenLists(1)
    qobj = gluNewQuadric()
    gluQuadricDrawStyle(qobj, GLU_FILL)
    gluQuadricNormals(qobj, GLU_SMOOTH)
    glNewList(gcylList, GL_COMPILE)
    # List Start
    glEnable(GL_COLOR_MATERIAL)
    glColor3f(0.0, 0.0, 1.0)
    glEnable(GL_LIGHT0)
    glEnable(GL_LIGHTING)
    glTranslatef(0.0, 0.0, -2.0)
    gluCylinder(qobj, 0.5, 0.5, 4.0, 15, 5)
    glDisable(GL_LIGHTING)
    glDisable(GL_LIGHT0)
    glDisable(GL_COLOR_MATERIAL)
    glEndList()
    # List End

    # if (gHaveWin32):
    # gStartTime = win32api.GetTickCount () 	# // Get the time the app started
    gStartTime = time.clock()  # // Get the time the app started

    return True  # // Initialization Went OK


# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
def ReSizeGLScene(Width, Height):
    if Height == 0:  # Prevent A Divide By Zero If The Window Is Too Small
        Height = 1

    glViewport(0, 0, Width, Height)  # Reset The Current Viewport And Perspective Transformation
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    # // field of view, aspect ratio, near and far
    # This will squash and stretch our objects as the window is resized.
    # Note that the near clip plane is 1 (hither) and the far plane is 1000 (yon)
    gluPerspective(45.0, float(Width) / float(Height), 1, 1000.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def DrawGLInfo():
    global gCamera, gFont, gFrames, gCurrentTime, gCurrentTime, gStartTime, gFPS

    projMatrix = glGetFloatv(GL_PROJECTION_MATRIX)  # // Grab the projection matrix
    modelMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)  # // Grab the modelview matrix

    # // Print out the cameras position
    glColor4f(1.0, 1.0, 1.0, 1.0)
    String = "m_Position............. = %.02f, %.02f, %.02f" % (
        gCamera.m_Position.x, gCamera.m_Position.y, gCamera.m_Position.z)
    gFont.glPrintf(10, 720, 1, String)

    # // Print out the cameras direction
    String = "m_DirectionVector...... = %.02f, %.02f, %.02f" % (
        gCamera.m_DirectionVector.i, gCamera.m_DirectionVector.j, gCamera.m_DirectionVector.k)
    gFont.glPrintf(10, 700, 1, String)

    # // Print out the light sources position
    String = "m_LightSourcePos....... = %.02f, %.02f, %.02f" % (
        gCamera.m_LightSourcePos.x, gCamera.m_LightSourcePos.y, gCamera.m_LightSourcePos.z)
    gFont.glPrintf(10, 680, 1, String)

    # // Print out the intersection point
    String = "ptIntersect............ = %.02f, %.02f, %.02f" % (
        gCamera.m_ptIntersect.x, gCamera.m_ptIntersect.y, gCamera.m_ptIntersect.z)
    gFont.glPrintf(10, 660, 1, String)

    # // Print out the vector that points from the light source to the camera
    String = "vLightSourceToCamera... = %.02f, %.02f, %.02f" % (
        gCamera.vLightSourceToCamera.i, gCamera.vLightSourceToCamera.j, gCamera.vLightSourceToCamera.k)
    gFont.glPrintf(10, 640, 1, String)

    # // Print out the vector that points from the light source to the intersection point.
    String = "vLightSourceToIntersect = %.02f, %.02f, %.02f" % (
        gCamera.vLightSourceToIntersect.i, gCamera.vLightSourceToIntersect.j, gCamera.vLightSourceToIntersect.k)
    gFont.glPrintf(10, 620, 1, String)

    # // Let everyone know the below matrix is the model view matrix
    String = "GL_MODELVIEW_MATRIX"
    gFont.glPrintf(10, 580, 1, String)

    # // Print out row 1 of the model view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (
        modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2], modelMatrix[0][3])
    gFont.glPrintf(10, 560, 1, String)

    # // Print out row 2 of the model view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (
        modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2], modelMatrix[1][3])
    gFont.glPrintf(10, 540, 1, String)

    # // Print out row 3 of the model view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (
        modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2], modelMatrix[2][3])
    gFont.glPrintf(10, 520, 1, String)

    # // Print out row 4 of the model view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (
        modelMatrix[3][0], modelMatrix[3][1], modelMatrix[3][2], modelMatrix[3][3])
    gFont.glPrintf(10, 500, 1, String)

    # // Let everyone know the below matrix is the projection matrix
    String = "GL_PROJECTION_MATRIX"
    gFont.glPrintf(10, 460, 1, String)

    # // Print out row 1 of the projection view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (projMatrix[0][0], projMatrix[0][1], projMatrix[0][2], projMatrix[0][3])
    gFont.glPrintf(10, 440, 1, String)

    # // Print out row 2 of the projection view matrix
    String = "%.02f, %.02f, %.02f, %.02f" % (projMatrix[1][0], projMatrix[1][1], projMatrix[1][2], projMatrix[1][3])
    gFont.glPrintf(10, 420, 1, String)

    # // Print out row 3 of the projection view matrix
    String = "%.02f, %.02f, %.03f, %.03f" % (projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3])
    gFont.glPrintf(10, 400, 1, String)

    # // Print out row 4 of the projection view matrix
    String = "%.02f, %.02f, %.03f, %.03f" % (projMatrix[3][0], projMatrix[3][1], projMatrix[3][2], projMatrix[3][3])
    gFont.glPrintf(10, 380, 1, String)

    # // Let everyone know the below values are the Frustum clipping planes
    gFont.glPrintf(10, 320, 1, "FRUSTUM CLIPPING PLANES")

    # // Print out the right clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[0][0], gCamera.m_Frustum[0][1], gCamera.m_Frustum[0][2], gCamera.m_Frustum[0][3])
    gFont.glPrintf(10, 300, 1, String)

    # // Print out the left clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[1][0], gCamera.m_Frustum[1][1], gCamera.m_Frustum[1][2], gCamera.m_Frustum[1][3])
    gFont.glPrintf(10, 280, 1, String)

    # // Print out the bottom clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[2][0], gCamera.m_Frustum[2][1], gCamera.m_Frustum[2][2], gCamera.m_Frustum[2][3])
    gFont.glPrintf(10, 260, 1, String)

    # // Print out the top clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[3][0], gCamera.m_Frustum[3][1], gCamera.m_Frustum[3][2], gCamera.m_Frustum[3][3])
    gFont.glPrintf(10, 240, 1, String)

    # // Print out the far clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[4][0], gCamera.m_Frustum[4][1], gCamera.m_Frustum[4][2], gCamera.m_Frustum[4][3])
    gFont.glPrintf(10, 220, 1, String)

    # // Print out the near clipping plane
    String = "%.02f, %.02f, %.02f, %.02f" % (
        gCamera.m_Frustum[5][0], gCamera.m_Frustum[5][1], gCamera.m_Frustum[5][2], gCamera.m_Frustum[5][3])
    gFont.glPrintf(10, 200, 1, String)

    if (gFrames >= 100):  # // if we are due for another FPS update
        # gCurrentTime = win32api.GetTickCount ()					# // Get the current time
        gCurrentTime = time.clock()  # // Get the current time
        DiffTime = gCurrentTime - gStartTime  # // Find the difference between the start and end times
        # gFPS = (gFrames / float (DiffTime)) * 1000.0					# // Compute the FPS
        gFPS = (gFrames / float(DiffTime))  # // Compute the FPS
        gStartTime = gCurrentTime  # // Set the current start time to the current time
        gFrames = 1  # // Set the number of frames to 1
    else:
        gFrames += 1  # // We are not due to for another update so add one to the frame count

    # // Print out the FPS
    String = "FPS %.02f" % (gFPS)
    gFont.glPrintf(10, 160, 1, String)
    return


def DrawGLScene():
    """ // Here's Where We Do All The Drawing """
    global gCamera, gcylList, ginfoOn

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # // Clear Screen And Depth Buffer
    glLoadIdentity()  # // Reset The Current Modelview Matrix

    # // We want our light source to be 50 units if front
    # // of the camera all the time to make it look like
    # // it is infinately far away from the camera. We only
    # // do this to the z coordinate because we want to see
    # // the flares adjust if we fly in a straight line.
    gCamera.m_LightSourcePos.z = gCamera.m_Position.z - 50.0


    # //##################### NEW STUFF ##########################
    # // Draw our cylinder and make it "do something"
    # // Of course we do that BEFORE testing for occlusion
    # // We need our depth buffer to be filled to check against occluder objects
    glPushMatrix()
    glLoadIdentity()
    glTranslatef(0.0, 0.0, -20.0)
    # glRotatef(win32api.GetTickCount () / 50.0, 0.3, 0.0, 0.0)
    # glRotatef(win32api.GetTickCount () / 50.0, 0.0, 0.5, 0.0)
    glRotatef((time.clock() * 1000.0) / 50.0, 0.3, 0.0, 0.0)
    glRotatef((time.clock() * 1000.0) / 50.0, 0.0, 0.5, 0.0)
    glCallList(gcylList)
    glPopMatrix()

    gCamera.SetPrespective()  # // Set our perspective/oriention on the world
    gCamera.RenderLensFlare()  # // Render the lens flare
    gCamera.UpdateFrustumFaster()  # // Update the frustum as fast as possible.

    # // Check to see if info has been toggled by 1,2
    if (gInfoOn):
        DrawGLInfo()  # // Info is on so draw the GL information.

    glutSwapBuffers()
    return True


# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)
def keyPressed(*args):
    global window, gCamera, gInfoOn, gFont, gcylList, qobj
    # If escape is pressed, kill everything.
    key = args[0]
    if key == ESCAPE:
        gFont.release()
        gCamera.release()
        gluDeleteQuadric(qobj)
        glDeleteLists(gcylList, 1)
        sys.exit()

    if key == 'W' or key == 'w':
        gCamera.ChangePitch(-0.2)  # // Pitch the camera up 0.2 degrees

    if key == 'S' or key == 's':
        gCamera.ChangePitch(0.2)  # // Pitch the camera down 0.2 degrees

    if key == 'D' or key == 'd':
        gCamera.ChangeHeading(0.2)  # // Yaw the camera to the left

    if key == 'A' or key == 'a':
        gCamera.ChangeHeading(-0.2)  # // Yaw the camera to the right

    if key == 'Z' or key == 'z':
        gCamera.m_ForwardVelocity = 0.01  # // Start moving the camera forward 0.01 units every frame

    if key == 'C' or key == 'c':
        gCamera.m_ForwardVelocity = -0.01  # // Start moving the camera backwards 0.01 units every frame

    if key == 'X' or key == 'x':
        gCamera.m_ForwardVelocity = 0.0  # // Stop the camera from moving.

    if args[0] == '1':
        gInfoOn = True  # // Toggle info on

    if args[0] == '2':
        gInfoOn = False  # // Toggle info off


def main():
    global window
    # pass arguments to init
    glutInit(sys.argv)

    # Select type of Display mode:
    # Double buffer
    # RGBA color
    # Alpha components supported
    # Depth buffer
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)

    # get a 640 x 480 window
    glutInitWindowSize(640, 480)

    # the window starts at the upper left corner of the screen
    glutInitWindowPosition(0, 0)

    # Okay, like the C version we retain the window id to use when closing, but for those of you new
    # to Python, remember this assignment would make the variable local and not global
    # if it weren't for the global declaration at the start of main.
    window = glutCreateWindow("Lens Flare Tutorial")

    # Register the drawing function with glut, BUT in Python land, at least using PyOpenGL, we need to
    # set the function pointer and invoke a function to actually register the callback, otherwise it
    # would be very much like the C version of the code.
    glutDisplayFunc(DrawGLScene)

    # Uncomment this line to get full screen.
    #glutFullScreen()

    # When we are doing nothing, redraw the scene.
    glutIdleFunc(DrawGLScene)

    # Register the function called when our window is resized.
    glutReshapeFunc(ReSizeGLScene)

    # Register the function called when the keyboard is pressed.
    glutKeyboardFunc(keyPressed)

    # We've told Glut the type of window we want, and we've told glut about
    # various functions that we want invoked (idle, resizing, keyboard events).
    # Glut has done the hard work of building up thw windows DC context and
    # tying in a rendering context, so we are ready to start making immediate mode
    # GL calls.
    # Call to perform inital GL setup (the clear colors, enabling modes, and most releveant -
    # consturct the displays lists for the bitmap font.
    InitGL(640, 480)

    # Start Event Processing Engine
    glutMainLoop()

# Print message to console, and kick off the main to get it rolling.
if __name__ == "__main__":
    print("Hit ESC key to quit.")
    main()

